package net.cyue.nest.nestjava.common.data.http;

import com.fasterxml.jackson.core.JsonProcessingException;
import net.cyue.nest.nestjava.common.util.InputUtil;
import net.cyue.nest.nestjava.common.util.JSONUtil;
import net.cyue.nest.nestjava.common.util.ReflectUtil;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class FormDataItem {
  private String contentDisposition = "form-data";
  private String contentType = "text/plain";
  private String name;
  private String filename;
  private byte[] data;

  @Override
  public String toString() {
    return "FormDataItem {\n" +
      "\tcontentDisposition='" + contentDisposition + "',\n" +
      "\tcontentType='" + contentType + "',\n" +
      "\tname='" + name + "',\n" +
      "\tfilename='" + filename + "',\n" +
      "\tdata='" + new String(data, StandardCharsets.ISO_8859_1) + "',\n" +
      '}';
  }

  public void setContentDispositionLine(String contentDispositionLine) {
    this.setContentDisposition(contentDispositionLine);
    String[] kv = contentDispositionLine.split(";");

    // 获取 name
    {
      String name = kv[1].trim().split("=")[1];
      // 去掉引号
      this.name = name.substring(1, name.length() - 1);
    }

    // 获取 filename
    if (kv.length == 3) {
      String filename = kv[2].trim().split("=")[1];
      // 去掉引号
      this.filename = filename.substring(1, filename.length() - 1);
    }
  }
  public void setContentDisposition(String contentDisposition) {
    if (contentDisposition.startsWith("Content-Disposition")) {
      contentDisposition = contentDisposition.substring(21).split(";")[0];
    }
    this.contentDisposition = contentDisposition;
  }
  public void setContentType(String contentType) {
    if (contentType.startsWith("Content-Type")) {
      contentType = contentType.substring(14);
    }
    this.contentType = contentType;
  }
  public void setData(byte[] bytes) {
    this.data = bytes;
  }

  public String getContentDisposition() {
    return this.contentDisposition;
  }
  public String getContentType() {
    return this.contentType;
  }
  public String getName() {
    return this.name;
  }
  public String getFilename() {
    return this.filename;
  }
  public byte[] getData() {
    return this.data;
  }
}

public class HTTPRequestBody extends HashMap<String, Object> {

  public HTTPRequestBody() {
    super();
  }
  public HTTPRequestBody(Map map) {
    super(map);
  }

  public <T> T toObject(Class<T> clz) {
    T instance = ReflectUtil.createInstance(clz);
    Field[] fields = clz.getDeclaredFields();
    for (final var f : fields) {
      final var value = this.get(f.getName());
      if (value == null) {
        continue;
      }
      ReflectUtil.setFieldValue(instance, f, value);
    }
    return instance;
  }
  public byte[] getBytes(String name) {
    return (byte[]) this.get(name);
  }
  public String getString(String name) {
    return (String) this.get(name);
  }
  public int getInt(String name) {
    return (int) this.get(name);
  }
  public float getFloat(String name) {
    return (float) this.get(name);
  }
  public double getDouble(String name) {
    return (double) this.get(name);
  }

  public static HTTPRequestBody fromJSONString(String json)
    throws JsonProcessingException
  {
    if (json == null || json.isEmpty() || json.isBlank()) {
      return new HTTPRequestBody();
    }
    final var map = JSONUtil.parseObject(json, Map.class);
    return new HTTPRequestBody(map);
  }

  public static HTTPRequestBody fromQueryString(String queryString) {
    if (queryString == null || queryString.isEmpty() || queryString.isBlank()) {
      return new HTTPRequestBody();
    }
    final var body = new HTTPRequestBody();
    final var params = queryString.split("&");
    for (final var p : params) {
      final var kv = p.split("=");
      if (kv.length != 2) {
        continue;
      }
      final var k = kv[0];
      final var v = kv[1];
      body.put(k, v);
    }
    return body;
  }

  public static HTTPRequestBody fromFormData(
    String boundary,
    InputStream input
  ) throws IOException {
    String formData = InputUtil.readString(input, StandardCharsets.ISO_8859_1);
    final String startString = "--" + boundary;
    final String endString = startString + "--";
    final String lf = "\r\n";
    final List<FormDataItem> dataItemList = new ArrayList<>();

    // System.out.println(formData);
    while (!formData.startsWith(endString)) {
      // ------WebKitFormBoundary6zSUvTdS6WkabSBL
      // Content-Disposition: form-data; name="data"
      //
      // data
      // ------WebKitFormBoundary6zSUvTdS6WkabSBL--

      FormDataItem item = new FormDataItem();

      // 去掉 ------WebKitFormBoundary6zSUvTdS6WkabSBL
      int contentDispositionStartIndex = formData.indexOf(lf) + lf.length();
      formData = formData.substring(contentDispositionStartIndex);
      // 获取 Content-Disposition
      int contentDispositionLineEndIndex = formData.indexOf(lf);
      String contentDispositionLine = formData.substring(0, contentDispositionLineEndIndex);
      formData = formData.substring(contentDispositionLineEndIndex + lf.length());
      byte[] contentDispositionBytes = contentDispositionLine.getBytes(StandardCharsets.ISO_8859_1);
      String contentDispositionLineUTF8 = new String(contentDispositionBytes, StandardCharsets.UTF_8);
      item.setContentDispositionLine(contentDispositionLineUTF8);

      // 获取Content-Type
      if (formData.startsWith("Content-Type:")) {
        int contentTypeEndIndex = formData.indexOf(lf);
        String contentTypeLine = formData.substring(0, contentTypeEndIndex);
        formData = formData.substring(contentTypeEndIndex + lf.length() * 2);
        item.setContentType(contentTypeLine);
      } else {
        formData = formData.substring(lf.length());
      }

      // 获取 data
      int dataEndIndex = formData.indexOf(lf + startString);
      String data = formData.substring(0, dataEndIndex);
      formData = formData.substring(dataEndIndex + lf.length());
      item.setData(data.getBytes(StandardCharsets.ISO_8859_1));

      dataItemList.add(item);
    }

    final var body = new HTTPRequestBody();
    for (final var item : dataItemList) {
      // System.out.println(item);
      body.put(item.getName(), item.getData());
    }
    return body;
  }
}

